﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Inspire.Classes.Business;
using System.Collections.ObjectModel;
using Inspire.Entities;
using Inspire.DataStructures;
using Inspire.Utility;
using Inspire.UIResources;
using Inspire.Classes.Common;

namespace Inspire.Windows
{
    /// <summary>
    /// Interaction logic for EditPatron.xaml
    /// </summary>
    public partial class EditPatron : Window
    {
        /// <summary>
        /// Get a reference to the PatronBusiness object.
        /// </summary>
        private PatronBusiness _patronBusiness = PatronBusiness.GetPatronBusiness();

        /// <summary>
        /// Get a reference to the Entity Context
        /// </summary>
        private InspireEntities _context = ContextProvider.GetContextProvider().GetContext();

        /// <summary>
        /// List backing the suggestions of Parent 1 field.
        /// </summary>
        private ObservableCollection<Patron> _parentList1 = new ObservableCollection<Patron>();
        public ObservableCollection<Patron> ParentList1
        {
            get
            {
                return this._parentList1;
            }
            set
            {
                this._parentList1 = value;
            }
        }

        /// <summary>
        /// List backing the suggestions of Parent 2 field.
        /// </summary>
        private ObservableCollection<Patron> _parentList2 = new ObservableCollection<Patron>();
        public ObservableCollection<Patron> ParentList2
        {
            get
            {
                return this._parentList2;
            }
            set
            {
                this._parentList2 = value;
            }
        }

        /// <summary>
        /// Opens the Edit Patron screen with the data for the currently selected patron loaded.
        /// </summary>
        /// <param name="editedPatron">(Patron) The Patron to be edited.</param>
        public EditPatron(Patron editedPatron)
        {
            #region Prepare Fields

            CustomFieldBusiness customFieldBusiness = CustomFieldBusiness.GetCustomFieldBusiness();

            // Set the reference for the edited patron
            this._editedPatron = editedPatron;

            // Detach the patron until we're ready to save.
            this._context.People.Detach(this._editedPatron);
            this._context.ObjectStateManager.ChangeObjectState(this._editedPatron, System.Data.EntityState.Detached);

            // Create the Custom Contact Field Types list for the user to create contact entries from
            this._contactFields = customFieldBusiness.GetCustomFieldTypesByCategory(Constants.CUSTOM_FIELD_PATRON_CONTACT);

            // Load the existing contact methods into the list to be displayed
            foreach(CustomField nextField in customFieldBusiness.GetCustomPatronFieldsByCategory(editedPatron, Constants.CUSTOM_FIELD_PATRON_CONTACT))
            {                    
                // Create and load the new contact method
                PatronContactMethod nextMethod = new PatronContactMethod();
                nextMethod.ExistingField = nextField;
                nextMethod.MethodFieldType = nextField.FieldType;
                nextMethod.MethodValue = nextField.ToString();

                this._contactMethods.Add(nextMethod);
            }

            // Build a list of the existing custom fields for this Patron
            List<CustomField> existingCustomFields = customFieldBusiness.GetCustomPatronFieldsByCategory(editedPatron, Constants.CUSTOM_FIELD_PATRON_CUSTOM);

            // Create the Custom Field Types structures list for the user to fill in
            foreach(CustomFieldType nextType in customFieldBusiness.GetCustomFieldTypesByCategory(Constants.CUSTOM_FIELD_PATRON_CUSTOM))
            {
                // Create the next "NewCustomFieldSource" to populate the custom fields list
                NewCustomFieldSource newSource = new NewCustomFieldSource();
                newSource.CustomFieldType = nextType;

                // See if this Patron has an existing custom field for this field type; if so, set the existing field and its value for this "NewCustomFieldSource"
                foreach (CustomField nextField in existingCustomFields)
                {
                    if (nextField.FieldType.Id == newSource.CustomFieldType.Id)
                    {
                        newSource.ExistingField = nextField;
                        newSource.FieldValue = nextField.GetData();
                        break;
                    }
                }

                // Add the "NewCustomFieldSource"
                this._customFields.Add(newSource);
            }

            #endregion

            InitializeComponent();
        }

        #region Basic Tab Fields

        // New Patron to Populate with Information
        private Patron _editedPatron = new Patron();

        /// <summary>
        /// The New Patron Entity to be created and persisted
        /// </summary>
        public Patron EditedPatron
        {
            get
            {
                return this._editedPatron;
            }
            set
            {
                this._editedPatron = value;
            }
        }

        #endregion

        #region Contact Tab Fields

        /// <summary>
        /// ContactFields: Expose the custom contact field types for patron contact information to the pageIndex.
        /// </summary>
        private List<CustomFieldType> _contactFields;
        public List<CustomFieldType> ContactFields
        {
            get
            {
                return this._contactFields;
            }
        }

        // List of Custom Contact Field Types and Values for this Patron
        private ObservableCollection<PatronContactMethod> _contactMethods = new ObservableCollection<PatronContactMethod>();

        /// <summary>
        /// The Contact Methods (Custom Contact Field Types and Values) for this Patron
        /// </summary>
        public ObservableCollection<PatronContactMethod> ContactMethods
        {
            get
            {
                return this._contactMethods;
            }
            set
            {
                this._contactMethods = value;
            }
        }

        // Store Contact Methods to be Deleted
        public LinkedList<PatronContactMethod> _deletedContactMethods = new LinkedList<PatronContactMethod>();


        #endregion

        #region Custom Tab Fields

        // List of Custom Fields for this Patron
        private ObservableCollection<NewCustomFieldSource> _customFields = new ObservableCollection<NewCustomFieldSource>();

        /// <summary>
        /// The Custom Field Type data structures for the Custom tab
        /// </summary>
        public ObservableCollection<NewCustomFieldSource> CustomFields
        {
            get
            {
                return this._customFields;
            }
            set
            {
                this._customFields = value;
            }
        }

        #endregion

        #region Event Handlers and General Methods

        /// <summary>
        /// Handle a button click for the Save button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSave_Click(object sender, RoutedEventArgs e)
        {
            // Get a reference to the context
            InspireEntities context = ContextProvider.GetContextProvider().GetContext();

            #region Validation
            // Validate all fields, even if unchanged
            if (!(errorProvider1.ValidateFinal()))
            {
                // Show a MessageBox with all errors
                string output = "";
                foreach (string error in this.errorProvider1.ErrorList)
                {
                    output += error + "\n";
                }

                // Display the Errors
                System.Windows.MessageBox.Show(output, "Validation Error", MessageBoxButton.OK, MessageBoxImage.Error);

                // Give Focus to the First Erroneous Field
                errorProvider1.FirstInvalidElement.Focus();

                // Exit this method to stop the save
                return;
            }
            #endregion

            #region Saving

            // Delete the removed contact methods
            if (this._deletedContactMethods.Count > 0)
            {
                foreach (PatronContactMethod next in this._deletedContactMethods)
                {
                    context.DeleteObject(next.ExistingField);
                }
            }

            // Ensure the Patron is saved. If not, alert the user.
            if (this._patronBusiness.SavePatron(this.EditedPatron, this._customFields, this._contactMethods) == null)
            {
                System.Windows.MessageBox.Show("An error occurred when saving. Please try saving again.", "Error.", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            // Close
            this.Close();

            #endregion
        }

        /// <summary>
        /// When the selection changes, look up the Patron ID for the selected person and set the Parent1 field of the new Patron to that ID.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void acParent_TextChanged(object sender, TextChangedEventArgs e)
        {
            int i = 0;
            AutoComplete parentAc = (AutoComplete)sender;
            IEnumerable<Patron> resultSet;
            string textValue = ((TextBox)e.Source).Text;
            string firstName = "", lastName = "";

            // Parse the text entered by the user
            for (i = 0; i < textValue.Length; i++)
            {
                if (textValue[i] == ' ') continue;

                if (lastName.Length > 0)
                {
                    firstName = textValue.Substring(i);
                    break;
                }

                if (textValue[i] == ',' || i == (textValue.Length - 1))
                {
                    lastName = textValue.Substring(0, i);
                    continue;
                }
            }

            // Update the option list based on a search with the user's text as the query
            resultSet = this._patronBusiness.SearchByName(firstName, lastName);
            if (resultSet != null && resultSet.Count() > 0)
            {
                parentAc.OptionList = resultSet.ToList<Object>();
                parentAc.ShowOptions = true;
            }
            else
            {
                parentAc.ShowOptions = false;
            }
        }

        /// <summary>
        /// Handle a button click for the Add Contact button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAddContact_Click(object sender, RoutedEventArgs e)
        {
            PatronContactMethod newMethod = new PatronContactMethod();
            newMethod.MethodValue = "";

            this._contactMethods.Add(newMethod);
        }

        /// <summary>
        /// Handle the Cancel button being clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        /// <summary>
        /// Handle a button click for the Remove Contact button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnRemoveContact_Click(object sender, RoutedEventArgs e)
        {
            if (this.dgContactInformation.SelectedCells.Count > 0)
            {
                DataGridCell cell = DataGridHelper.GetCell(this.dgContactInformation.SelectedCells[0]);
                PatronContactMethod method = (PatronContactMethod)DataGridHelper.GetRowItem(cell);

                // If this method is already persisted to the Database, remove it
                if (method.ExistingField != null)
                {
                    this._deletedContactMethods.AddLast(method);
                    this._contactMethods.Remove(method);
                }

                // Remove the method from the list
                this._contactMethods.Remove(method);
            }
        }

        /// <summary>
        /// If a cell is selected in our custom fields grid, activate it for editing.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dgCustomFields_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
        {
            if (e.AddedCells.Count < 1) return;
            if (e.AddedCells[0].Column.DisplayIndex == 1)
            {
                dgCustomFields.BeginEdit();
            }
        }

        /// <summary>
        /// Handles switching tabs if user uses the nav buttons at the bottom of the screen
        /// </summary>
        /// <param name="sender">(Button) oject sending the event</param>
        /// <param name="e">(RoutedEventArgs) Arguments from the event</param>
        private void NavigateButtons_OnClick(object sender, RoutedEventArgs e)
        {
            // Handle Changing Tabs for Previous Button
            if (((Button)sender).Name.Equals("btnPrevious"))
            {
                // If it's already at the leftmost tab, do nothing
                if (this.tabControlEditPatron.SelectedIndex == 0)
                {
                    return;
                }
                else
                {
                    // If the first tab is about to be shown, disable the Previous button
                    if (this.tabControlEditPatron.SelectedIndex == 1)
                    {
                        this.btnPrevious.IsEnabled = false;

                        // Adjust the Text of the Previous Button
                        this.lblPreviousButton.Text = ((TabItem)this.tabControlEditPatron.Items[this.tabControlEditPatron.SelectedIndex - 1]).Header.ToString();
                    }
                    else
                    {
                        // Adjust the Text of the Previous Button
                        this.lblPreviousButton.Text = ((TabItem)this.tabControlEditPatron.Items[this.tabControlEditPatron.SelectedIndex - 2]).Header.ToString();
                    }

                    // Adjust the Text of the Next Button
                    this.lblNextButton.Text = ((TabItem)this.tabControlEditPatron.Items[this.tabControlEditPatron.SelectedIndex]).Header.ToString();

                    // Adjust the Visible Tab
                    this.tabControlEditPatron.SelectedIndex = this.tabControlEditPatron.SelectedIndex - 1;

                    // Ensure the Next Button is Enabled
                    this.btnNext.IsEnabled = true;
                }
            }

            // Handle Changing Tabs for the Next Button
            if (((Button)sender).Name.Equals("btnNext"))
            {
                // If it's already at the rightmost tab, do nothing
                if (this.tabControlEditPatron.SelectedIndex == this.tabControlEditPatron.Items.Count - 1)
                {
                    return;
                }
                else
                {
                    // If the last tab is about to be shown, disable the Next button
                    if (this.tabControlEditPatron.SelectedIndex == (this.tabControlEditPatron.Items.Count - 2))
                    {
                        this.btnNext.IsEnabled = false;

                        // Adjust the Text of the Next Button
                        this.lblNextButton.Text = ((TabItem)this.tabControlEditPatron.Items[this.tabControlEditPatron.SelectedIndex + 1]).Header.ToString();
                    }
                    else
                    {
                        // Adjust the Text of the Next Button
                        this.lblNextButton.Text = ((TabItem)this.tabControlEditPatron.Items[this.tabControlEditPatron.SelectedIndex + 2]).Header.ToString();
                    }

                    // Adjust the Text of the Previous Button
                    this.lblPreviousButton.Text = ((TabItem)this.tabControlEditPatron.Items[this.tabControlEditPatron.SelectedIndex]).Header.ToString();

                    // Adjust the Visible Tab
                    this.tabControlEditPatron.SelectedIndex = this.tabControlEditPatron.SelectedIndex + 1;

                    // Ensure the Previous Button is Enabled
                    this.btnPrevious.IsEnabled = true;
                }
            }
        }

        /// <summary>
        /// Handles updating the Forward and Backward Control Buttons when a new tab is selected
        /// on the TabControl.
        /// </summary>
        /// <param name="sender">(TabControl) object sending the event</param>
        /// <param name="e">(SelectionChangedEventArgs) Arguments from the event</param>
        private void tabControlEditPatrons_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // If it's the first Tab
            if (this.tabControlEditPatron.SelectedIndex == 0)
            {
                // Adjust the Text of the Next & Previous Buttons
                this.lblPreviousButton.Text = ((TabItem)this.tabControlEditPatron.Items[0]).Header.ToString();
                this.lblNextButton.Text = ((TabItem)this.tabControlEditPatron.Items[1]).Header.ToString();

                // Adjust the IsEnabled of the Next & Previous Buttons
                this.btnPrevious.IsEnabled = false;
                this.btnNext.IsEnabled = true;
            }

            // If it's an in-between Tab
            if (this.tabControlEditPatron.SelectedIndex > 0 && this.tabControlEditPatron.SelectedIndex < (this.tabControlEditPatron.Items.Count - 1))
            {
                // Adjust the Text of the Next & Previous Buttons
                this.lblPreviousButton.Text = ((TabItem)this.tabControlEditPatron.Items[this.tabControlEditPatron.SelectedIndex - 1]).Header.ToString();
                this.lblNextButton.Text = ((TabItem)this.tabControlEditPatron.Items[this.tabControlEditPatron.SelectedIndex + 1]).Header.ToString();

                // Adjust the IsEnabled of the Next & Previous Buttons
                this.btnPrevious.IsEnabled = true;
                this.btnNext.IsEnabled = true;
            }

            // If it's the last Tab
            if (this.tabControlEditPatron.SelectedIndex == (this.tabControlEditPatron.Items.Count - 1))
            {
                // Adjust the Text of the Next & Previous Buttons
                this.lblPreviousButton.Text = ((TabItem)this.tabControlEditPatron.Items[this.tabControlEditPatron.SelectedIndex - 1]).Header.ToString();
                this.lblNextButton.Text = ((TabItem)this.tabControlEditPatron.Items[this.tabControlEditPatron.SelectedIndex]).Header.ToString();

                // Adjust the IsEnabled of the Next & Previous Buttons
                this.btnPrevious.IsEnabled = true;
                this.btnNext.IsEnabled = false;
            }
        }

        #endregion

        /// <summary>
        /// Re-enable the owner window when this dialog is closed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Window_Closed(object sender, EventArgs e)
        {
            this.Owner.IsEnabled = true;
        }

        /// <summary>
        /// Handle displaying the warning message and delete button when this checkbox is checked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DeletePatron_Checked(object sender, RoutedEventArgs e)
        {
            this.gridDeletePatron.Visibility = System.Windows.Visibility.Visible;
        }

        /// <summary>
        /// Handle hiding the warning message and delete button when this checkbox is unchecked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DeletePatron_Unchecked(object sender, RoutedEventArgs e)
        {
            this.gridDeletePatron.Visibility = System.Windows.Visibility.Collapsed;
        }

        /// <summary>
        /// Handle deleting this patron and closing the dialog when the delete button is checked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DeletePatron_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }
    }
}
